/*
 * Copyright (c) 2017 Actions Semiconductor Co., Ltd
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief link memory address overlay helper
 */

#include <kernel.h>
#include <string.h>
#include <misc/printk.h>
#include <section_overlay.h>

#define SYS_LOG_DOMAIN "overlay"
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#include <logging/sys_log.h>

struct overlay_info {
	u32_t	idcode;

	u32_t	code_vma;
	u32_t	code_size;
	u32_t	code_lma;

	u32_t	data_vma;
	u32_t	data_size;
	u32_t	data_lma;

	u32_t	bss_vma;
	u32_t	bss_size;
};

struct overlay_table
{
	u32_t magic;
	u32_t count;

	const struct overlay_info items[0];
};

/* overlay table is dynamic generated by linker */
extern const struct overlay_table __overlay_table;

static int check_overlay_table(const struct overlay_table *tbl)
{
	if (!tbl || tbl->magic != OVERLAY_TABLE_MAGIC || !tbl->count)
		return -1;

	return 0;
}

void overlay_dump(void)
{
	const struct overlay_info *ovl;
	int i, cnt;

	if (check_overlay_table(&__overlay_table)) {
		printk("invalid overlay table\n");
		return;
	}

	cnt = __overlay_table.count;

	printk("section overlay, total count %d\n", cnt);

	for (i = 0; i < cnt; i++) {
		ovl = &__overlay_table.items[i];

		printk(" -- item %d --\n", i);
		printk("      idcode: %d\n", ovl->idcode);
		printk("    code_vma: 0x%08x\n", ovl->code_vma);
		printk("   code_size: 0x%08x\n", ovl->code_size);
		printk("    code_lma: 0x%08x\n", ovl->code_lma);
		printk("    data_vma: 0x%08x\n", ovl->data_vma);
		printk("   data_size: 0x%08x\n", ovl->data_size);
		printk("    data_lma: 0x%08x\n", ovl->data_lma);
		printk("     bss_vma: 0x%08x\n", ovl->bss_vma);
		printk("    bss_size: 0x%08x\n", ovl->bss_size);
	}

}

int overlay_section_init(u32_t idcode)
{
	const struct overlay_info *ovl = NULL;
	int i, cnt;

	SYS_LOG_DBG("init overlay 0x%x", idcode);

	if (check_overlay_table(&__overlay_table)) {
		SYS_LOG_WRN("invalid overlay table\n");
		return -1;
	}

	cnt = __overlay_table.count;
	for (i = 0; i < cnt; i++) {
		ovl = &__overlay_table.items[i];
		if (idcode == ovl->idcode)
			break;
	}

	if (cnt == i) {
		SYS_LOG_WRN("cannot found overlay for idcode 0x%x\n", idcode);
		return -1;
	}

	if (ovl->code_size) {
		SYS_LOG_DBG("overlay id:0x%x: copy code from 0x%x to 0x%x, size 0x%x\n",
			idcode, ovl->code_lma, ovl->code_vma, ovl->code_size);
		memcpy((char *)ovl->code_vma, (char *)ovl->code_lma, ovl->code_size);
	}

	if (ovl->data_size) {
		SYS_LOG_DBG("overlay id:0x%x: copy data from 0x%x to 0x%x, size 0x%x\n",
			idcode, ovl->data_lma, ovl->data_vma, ovl->data_size);
		memcpy((char *)ovl->data_vma, (char *)ovl->data_lma, ovl->data_size);
	}

	if (ovl->bss_size) {
		SYS_LOG_DBG("overlay id:0x%x: init bss from 0x%x, size 0x%x\n",
			idcode,  ovl->bss_vma, ovl->bss_size);
		memset((char *)ovl->bss_vma, 0x0, ovl->bss_size);
	}

	SYS_LOG_DBG("init overlay 0x%x ok", idcode);
	return 0;
}
